Usage of db-migrate

To use db-migrate, you call it via the command line. When entering only the command without paramaters you will see something like this:

Usage: db-migrate [up|down|reset|create|db] [[dbname/]migrationName|all] [options]

Down migrations are run in reverse run order, so migrationName is ignored for down migrations.
Use the --count option to control how many down migrations are run (default is 1).

Options:
  --env, -e                   The environment to run the migrations under.    [default: "dev"]
  --migrations-dir, -m        The directory containing your migration files.  [default: "./migrations"]
  --count, -c                 Max number of migrations to run.
  --dry-run                   Prints the SQL but doesn't run it.              [boolean]
  --verbose, -v               Verbose mode.                                   [default: false]
  --config                    Location of the database.json file.             [default: "./database.json"]
  --force-exit                Call system.exit() after migration run          [default: false]
  --sql-file                  Create sql files for up and down.               [default: false]
  --coffee-file               Create a coffeescript migration file            [default: false]
  --migration-table           Set the name of the migration table.
  --table, --migration-table                                                  [default: "migrations"]

Creating Migrations

To create a migration, execute db-migrate create with a title. node-db-migrate will create a node module within ./migrations/ which contains the following two exports:

exports.up = function (db, callback) {
  callback();
};

exports.down = function (db, callback) {
  callback();
};

All you have to do is populate these, invoking callback() when complete, and you are ready to migrate!

For example:

$ db-migrate create add-pets
$ db-migrate create add-owners

The first call creates ./migrations/20111219120000-add-pets.js, which we can populate:

exports.up = function (db, callback) {
  db.createTable('pets', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  }, callback);
};

exports.down = function (db, callback) {
  db.dropTable('pets', callback);
};

The second creates ./migrations/20111219120005-add-owners.js, which we can populate:

exports.up = function (db, callback) {
  db.createTable('owners', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  }, callback);
};

exports.down = function (db, callback) {
  db.dropTable('owners', callback);
};

Executing multiple statements against the database within a single migration requires a bit more care. You can either nest the migrations like:

exports.up = function (db, callback) {
  db.createTable('pets', {
    id: { type: 'int', primaryKey: true },
    name: 'string'
  }, createOwners);

  function createOwners(err) {
    if (err) { callback(err); return; }
    db.createTable('owners', {
      id: { type: 'int', primaryKey: true },
      name: 'string'
    }, callback);
  }
};

exports.down = function (db, callback) {
  db.dropTable('pets', function(err) {
    if (err) { callback(err); return; }
    db.dropTable('owners', callback);
  });
};

or use the async library to simplify things a bit, such as:

var async = require('async');

exports.up = function (db, callback) {
  async.series([
    db.createTable.bind(db, 'pets', {
      id: { type: 'int', primaryKey: true },
      name: 'string'
    }),
    db.createTable.bind(db, 'owners', {
      id: { type: 'int', primaryKey: true },
      name: 'string'
    });
  ], callback);
};

exports.down = function (db, callback) {
  async.series([
    db.dropTable.bind(db, 'pets'),
    db.dropTable.bind(db, 'owners')
  ], callback);
};

Using files for sqls

If you prefer to use sql files for your up and down statements, you can use the --sql-file option to automatically generate these files and the javascript code that load them.

For example:

$ db-migrate create add-people --sql-file

This call creates 3 files:

./migrations/20111219120000-add-people.js
./migrations/sqls/20111219120000-add-people-up.sql
./migrations/sqls/20111219120000-add-people-down.sql

The sql files will have the following content:

/* Replace with your SQL commands */

And the javascript file with the following code that load these sql files:

var dbm;
var type;
var fs = require('fs');
var path = require('path');

/**
  * We receive the dbmigrate dependency from dbmigrate initially.
  * This enables us to not have to rely on NODE_PATH.
  */
exports.setup = function(options) {
  dbm = options.dbmigrate;
  type = dbm.datatype;
};

exports.up = function(db, callback) {
  var filePath = path.join(__dirname + '/sqls/20111219120000-add-people-up.sql');
  fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
    if (err) return console.log(err);
    db.runSql(data, function(err) {
      if (err) return console.log(err);
      callback();
    });
  });
};

exports.down = function(db, callback) {
  var filePath = path.join(__dirname + '/sqls/20111219120000-add-people-down.sql');
  fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
    if (err) return console.log(err);
    db.runSql(data, function(err) {
      if (err) return console.log(err);
      callback();
    });
  });
};

Making it as default

To not need to always specify the sql-file option in your db-migrate create commands, you can set a property in your database.json as follows:

{
    "dev": {
      "host": "localhost",
    ...
  },
    "sql-file" : true
}

Running Migrations

When first running the migrations, all will be executed in sequence. A table named migrations will also be created in your database to track which migrations have been applied.

  $ db-migrate up
  [INFO] Processed migration 20111219120000-add-pets
  [INFO] Processed migration 20111219120005-add-owners
  [INFO] Done

Subsequent attempts to run these migrations will result in the following output

  $ db-migrate up
  [INFO] No migrations to run
  [INFO] Done

If we were to create another migration using db-migrate create, and then execute migrations again, we would execute only those not previously executed:

  $ db-migrate up
  [INFO] Processed migration 20111220120210-add-kennels
  [INFO] Done

You can also run migrations incrementally by specifying a date substring. The example below will run all migrations created on or before December 19, 2011:

  $ db-migrate up 20111219
  [INFO] Processed migration 20111219120000-add-pets
  [INFO] Processed migration 20111219120005-add-owners
  [INFO] Done

You can also run a specific number of migrations with the -c option:

  $ db-migrate up -c 1
  [INFO] Processed migration 20111219120000-add-pets
  [INFO] Done

All of the down migrations work identically to the up migrations by substituting the word down for up.

Seeder Introduction

This page should give you an overview, of what seeders actually are and what you can expect of them. To also tell some of the practices that make things easier for you. This is really just an overview, take a look at the API description for all details.

General operations

The seeder consists of method such as update, insert and data manipulation methods.

update( table, data, searchclause/id )

While selecting data, that might not belong to your table yet, there are different kinds of helpers, that are going to help you, while propagating your database.

There are update helper and a defined where clause helper, included in functions like select or update. An example would be this:

exports.up = function( db ) {

  return db.update( 'test', { name: 'cedric', surname: 'camelot' }, 12 );
};

This is going to update the 12th record of the table test, which has got a new name cedric camelot. There are more examples in our example project, which you can find here. For an overview of all available options and helpers take a look at the API Description.

select( table, data, searchclause/id )

This helper is an option for you to use, if you want your seeders to be generic and usable across different DBMS's. No matter if NoSQL or SQL or NewSQL, it should behave similar in every case.

exports.up = function( up ) {

  return db.select( 'test', [ 'id', 'name', 'surname' ], { bff: 12 } );
};

This example would result in the id, name and surname of the guys who have number 12 as their bff.

Common Helpers

These helpers are here for your when there are cases when you're working with db-migrate and db-migrate could help you accomplishing your work more efficiently and more easy.

_l( options )

The lookup helper, a common task is that you have the value, but you do not have necessarily the id of this value. This id might change for some reason, also it should better never ever change but that by side, or you just really do not know it. As far as this column is referencing a value in another table, this helper is the one you are looking for.

Options object

  • t (shortcut: t) - DB to lookup the value
  • defaults only on compliance to db guidelines
  • data column (shortcut: c) - Column with the corresponding value
  • defaults to 'value'
  • id column (shortcut: k) - corresponding id column
  • defaults to 'id'

DB-Migrate - DB Guidelines

There are some common practices that makes the work with DB-Migrate even easier. These practices fit with already known best practices, that are not DB-Migrate related. Also you can adjust many things to work with different standards.

Column references

Some may always call them foreign keys... However, you can lookup without ctually providing a database.

If a column reference is referencing a value in the table test_db_arch, you would name the column test_db_arch_id.

Beside this you can also do things like: ref_test_db_arch_attribute test_db_arch_attribute_fk

This would receive the value from the table test_db_arch and get the value from the attribute column in this table. We always default the id column to be named like id, there is no shorthand via the naming. Instead you can design a seed like the following example:

exports.up = function( db ) {

  return db.insert( 'test', {
    ref_test_db_arch_attribute: _l( { id: 'identifier' } ),
    test_db_arch_id: _l(),
    common_value: _l( { t: 'test_db_arch' } ),
    arch_id: _()
  } );
};

All of these, except arch_id are going to resolve to the value from the test_db_arch table, referenced as id column. You can however also make arch_id of this example work, you can register common shorthands. There are many examples where you have columns of tables with long names, like pseudo_type. Many do name their columns like pt_id, you can now register this column via:

db-migrate register alias pt pseudo_type

This makes you unable to use a table named pt, thus you really should know what you're doing. Also to mention, it is recommended to have as less aliases as possible, only use them if they're common and used all over your project and by this means known to be used in this project.

Change management

One of the most important things is to know when who is updating why what where, if you do not known exactly what happens in parts of your project you're never going to be able to do such things as zero downtime deployments or automation of the deployment in general.